﻿using log4net;
using Microsoft.Win32;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration.Install;
using System.Management;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Text;

namespace ST.Common.Util
{
	public class ServiceHelper
	{
		[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		private static extern IntPtr OpenSCManager(string machineName, string databaseName, uint dwAccess);

		[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		private static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);

		[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		private static extern Boolean ChangeServiceConfig(
			IntPtr hService,
			UInt32 nServiceType,
			UInt32 nStartType,
			UInt32 nErrorControl,
			String lpBinaryPathName,
			String lpLoadOrderGroup,
			IntPtr lpdwTagId,
			[In] char[] lpDependencies,
			String lpServiceStartName,
			String lpPassword,
			String lpDisplayName);

		[DllImport("advapi32.dll", EntryPoint = "CloseServiceHandle")]
		private static extern int CloseServiceHandle(IntPtr hSCObject);

		private const uint SC_MANAGER_CONNECT = 0x0001;
		private const uint SERVICE_QUERY_CONFIG = 0x00000001;
		private const uint SERVICE_CHANGE_CONFIG = 0x00000002;
		private const uint SERVICE_NO_CHANGE = 0xFFFFFFFF;

		private static ILog _Log = LogManager.GetLogger(typeof(ServiceHelper));

		/// <summary>
		/// 检查服务存在的存在性
		/// </summary>
		/// <param name=" NameService ">服务名</param>
		/// <returns>存在返回 true,否则返回 false;</returns>
		public static bool IsServiceExisted(string serviceName)
		{
			ServiceController[] services = ServiceController.GetServices();
			foreach (ServiceController s in services)
			{
				if (string.Compare(s.ServiceName, serviceName, true) == 0)
				{
					return true;
				}
			}
			return false;
		}

		/// <summary>
		/// 安装Windows服务
		/// </summary>
		/// <param name="stateSaver">集合</param>
		/// <param name="filepath">程序文件路径</param>
		public static bool InstallService(IDictionary savedState, string serviceName, string serviceFilePath, bool autoStart)
		{
			bool success = false;
			try
			{
#if DEBUG
				_Log.Debug("开始安装服务" + serviceName + "，路径：" + serviceFilePath + "...");
#endif
				if (!IsServiceExisted(serviceName))
				{
					AssemblyInstaller installer = new AssemblyInstaller();
					installer.UseNewContext = true;
					installer.Path = serviceFilePath;
					installer.Install(savedState);
					installer.Commit(savedState);
					installer.Dispose();
				}

				if (autoStart)
				{
					SetServiceStartType(2, serviceName);//修改服务启动方式为自动
				}
				success = true;
			}
			catch (Exception ex)
			{
				_Log.Error(ex);
			}
			return success;
		}

		/// <summary>
		/// 安装Windows服务并设置启动方式为自动
		/// </summary>
		/// <param name="savedState"></param>
		/// <param name="serviceName"></param>
		/// <param name="serviceFileName"></param>
		/// <returns></returns>
		public static bool InstallService(IDictionary savedState, string serviceName, string serviceFileName)
		{
			return InstallService(savedState, serviceName, serviceFileName, true);
		}

		/// <summary>
		/// 卸载Windows服务
		/// </summary>
		/// <param name="filepath">程序文件路径</param>
		public static bool UninstallService(IDictionary savedState, string serviceName, string serviceFilePath)
		{
			bool success = false;
			try
			{
#if DEBUG
				_Log.Debug("开始卸载服务" + serviceName + "...");
#endif
				ServiceController sc = new ServiceController(serviceName);
				if (sc.Status.Equals(ServiceControllerStatus.Running))
				{
					sc.Stop();
					sc.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromMinutes(1));
				}
				AssemblyInstaller installer = new AssemblyInstaller();
				installer.UseNewContext = true;
				installer.Path = serviceFilePath;
				installer.Uninstall(savedState);
				installer.Dispose();
				success = true;
			}
			catch (Exception ex)
			{
				_Log.Error(ex);
			}
			return success;

		}

		/// <summary>
		/// 判断某个Windows服务是否启动
		/// </summary>
		/// <returns></returns>
		public static bool IsServiceStart(string serviceName)
		{
			bool started = false;
			try
			{
				ServiceController sc = new ServiceController(serviceName);
				if (sc.Status.Equals(ServiceControllerStatus.Running) || sc.Status.Equals(ServiceControllerStatus.StartPending))
				{
					started = true;
				}
			}
			catch (Exception ex)
			{
				_Log.Error(ex);
			}
			return started;
		}

		/// <summary>  
		/// 修改服务的启动项 2为自动,3为手动,4为禁用 
		/// </summary>  
		/// <param name="startType"></param>  
		/// <param name="serviceName"></param>  
		/// <returns></returns>  
		public static bool SetServiceStartType(int startType, string serviceName)
		{
			bool success = false;
			try
			{
				#region 通过注册表修改服务启动方式，如果服务禁用修改成自动后还是不能启动
				//RegistryKey regKey = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Services\" + serviceName, true);
				//if (regKey != null)
				//{
				//	regKey.SetValue("Start", startType);
				//	regKey.Close();
				//	success = true;
				//}
				#endregion

				#region 调用API方式可以立刻生效
				//IntPtr scmHandle = OpenSCManager(null, null, SC_MANAGER_CONNECT);//Obtain a handle to the service control manager database
				//if (scmHandle == IntPtr.Zero)
				//{
				//	throw new Exception("Failed to obtain a handle to the service control manager database.");
				//}

				//IntPtr serviceHandle = OpenService(scmHandle, serviceName, SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);//Obtain a handle to the specified windows service
				//if (serviceHandle == IntPtr.Zero)
				//{
				//	throw new Exception(string.Format("Failed to obtain a handle to service \"{0}\".", serviceName));
				//}

				//bool changeServiceSuccess = ChangeServiceConfig(serviceHandle, SERVICE_NO_CHANGE, (uint)startType, SERVICE_NO_CHANGE, null, null, IntPtr.Zero, null, null, null, null);

				//if (!changeServiceSuccess)
				//{
				//	string msg = string.Format("Failed to update service configuration for service \"{0}\". ChangeServiceConfig returned error {1}.", serviceName, Marshal.GetLastWin32Error().ToString());
				//	throw new Exception(msg);
				//}

				////Clean up
				//if (scmHandle != IntPtr.Zero) CloseServiceHandle(scmHandle);
				//if (serviceHandle != IntPtr.Zero) CloseServiceHandle(serviceHandle);
				#endregion

				#region 通过WMI方式可以立刻生效
				using (var mo = new ManagementObject(string.Format("Win32_Service.Name=\"{0}\"", serviceName)))
				{
					//Boot:Device driver started by the operating system loader. This value is valid only for driver services.
					//System:Device driver started by the operating system initialization process. This value is valid only for driver services.
					//Automatic:Service to be started automatically by the service control manager during system startup.
					//Manual:Service to be started by the service control manager when a process calls the StartService method.
					//Disabled:Service that can no longer be started.
					mo.InvokeMethod("ChangeStartMode", new object[] { "Automatic" });
				}
				#endregion
			}
			catch (Exception ex)
			{
				_Log.Error(ex);
			}
			return success;
		}

		/// <summary>
		/// 启动服务
		/// </summary>
		/// <param name="serviceName"></param>
		/// <returns></returns>
		public static bool StartService(string serviceName)
		{
			bool success = false;
			try
			{
#if DEBUG
				_Log.Debug("开始启动服务" + serviceName + "...");
#endif
				ServiceController sc = null;
				ServiceController[] services = ServiceController.GetServices();
				foreach (ServiceController s in services)
				{
					if (string.Compare(s.ServiceName, serviceName, true) == 0)
					{
						sc = s;
						break;
					}
				}

				if (sc == null)
				{
					throw new Exception("服务" + serviceName + "未安装");
				}

				if (sc.Status != System.ServiceProcess.ServiceControllerStatus.Running && sc.Status != System.ServiceProcess.ServiceControllerStatus.StartPending)
				{
					sc.Start();
					sc.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromMinutes(1));
				}
				if (sc.Status == System.ServiceProcess.ServiceControllerStatus.Running)
				{
					success = true;
				}
			}
			catch (Exception ex)
			{
				_Log.Error(ex);
			}
			return success;
		}

		/// <summary>
		/// 停止服务
		/// </summary>
		/// <param name="serviceName"></param>
		/// <returns></returns>
		public static bool StopService(string serviceName)
		{
			bool success = false;
			try
			{
#if DEBUG
				_Log.Debug("开始停止服务" + serviceName + "...");
#endif
				ServiceController sc = null;
				ServiceController[] services = ServiceController.GetServices();
				foreach (ServiceController s in services)
				{
					if (string.Compare(s.ServiceName, serviceName, true) == 0)
					{
						sc = s;
						break;
					}
				}

				if (sc == null)
				{
					throw new Exception("服务" + serviceName + "未安装");
				}
				if (sc.Status == ServiceControllerStatus.Running)
				{
					sc.Stop();
					sc.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromMinutes(1));
				}
				if (sc.Status == System.ServiceProcess.ServiceControllerStatus.Stopped)
				{
					success = true;
				}
			}
			catch (Exception ex)
			{
				_Log.Error(ex);
			}
			return success;
		}

		/// <summary>
		/// 重启服务
		/// </summary>
		/// <param name="serviceName"></param>
		/// <returns></returns>
		public static bool RestartService(string serviceName)
		{
			bool success = false;
			try
			{
#if DEBUG
				_Log.Debug("开始重启服务" + serviceName + "...");
#endif
				ServiceController sc = null;
				ServiceController[] services = ServiceController.GetServices();
				foreach (ServiceController s in services)
				{
					if (string.Compare(s.ServiceName, serviceName, true) == 0)
					{
						sc = s;
						break;
					}
				}

				if (sc == null)
				{
					throw new Exception("服务" + serviceName + "未安装");
				}

				if (sc.Status != System.ServiceProcess.ServiceControllerStatus.Stopped && sc.Status != System.ServiceProcess.ServiceControllerStatus.StopPending)
				{
					sc.Stop();
					sc.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromMinutes(1));
				}
				if (sc.Status == ServiceControllerStatus.Stopped)
				{
					sc.Start();
					sc.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromMinutes(1));

					if (sc.Status == System.ServiceProcess.ServiceControllerStatus.Running)
					{
						success = true;
					}
				}
			}
			catch (Exception ex)
			{
				_Log.Error(ex);
			}
			return success;
		}
	}
}
